/*******************************************************************************

  Pilot Intelligence Library
    https://gitee.com/pi-lab

*******************************************************************************/

#pragma once

#include <string.h>
#include <stdint.h>
#include <string>
#include <vector>


namespace pi {


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// choose TCP、UDP or multicast UDP.
enum RSocketType
{
    SOCKET_TCP,                         ///< TCP
    SOCKET_UDP,                         ///< UDP
    SOCKET_UDP_MULTICAST,               ///< multicast UDP
};


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

///
/// \brief The RSocketAddress class
///
/// Multicast address:
///     https://en.wikipedia.org/wiki/Multicast_address
///
class RSocketAddress
{
public:
    RSocketAddress() {
        port = -1;
        addr_inet = 0;
        type = SOCKET_TCP;
        memset(addr_str, 0, sizeof(addr_str));
    }

    ~RSocketAddress() {
        port = -1;
        addr_inet = 0;
        type = SOCKET_TCP;
        memset(addr_str, 0, sizeof(addr_str));
    }

public:
    int         port;                   ///> port number
    uint32_t    addr_inet;              ///> uint32_t address
    char        addr_str[32];           ///> address string
    RSocketType type;                   ///> socket type
};


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

///
/// \brief The RSocket class support TCP/UDP
///
class RSocket
{
public:
    RSocket();
    virtual ~RSocket();

    ////////////////////////////////////////////////////
    /// start server or client
    ////////////////////////////////////////////////////

    ///
    /// \brief startServer
    ///
    /// \param port             - port number
    /// \param t                - socket type
    ///
    /// \return
    ///        0                - success
    ///        other            - failed
    ///
    int startServer(int port, RSocketType t=SOCKET_TCP);

    ///
    /// \brief startServer
    ///
    /// \param addr             - host or group address
    /// \param port             - port number
    /// \param t                - socket type
    ///
    /// \return
    ///        0                - success
    ///        other            - failed
    ///
    int startServer(const std::string& addr, int port, RSocketType t=SOCKET_UDP_MULTICAST);


    ///
    /// \brief startClient
    ///
    /// \param host             - pair host address or group address
    /// \param port             - port number
    /// \param t                - socket type
    ///
    /// \return
    ///        0                - success
    ///        other            - failed
    ///
    int startClient(const std::string& host, int port, RSocketType t=SOCKET_TCP);
    int setRemoteAddr(const std::string& host, int port);
    ///
    /// \brief startClient
    ///
    /// \param addr             - host or group address
    /// \param port             - port number
    /// \param t                - socket type
    ///
    /// \return
    ///        0                - success
    ///        other            - failed
    ///
    int startClient(uint32_t addr, int port, RSocketType t=SOCKET_TCP);


    ////////////////////////////////////////////////////
    /// Data Transmission
    ////////////////////////////////////////////////////

    ///
    /// \brief send data to remote host
    ///
    /// \param dat              - data buffer
    /// \param len              - data length
    ///
    /// \return
    ///        > 0              - actual sended bytes
    ///        <= 0             - failed
    ///
    int send(const char *dat, int len);

    ///
    /// \brief send string to remote host
    ///
    /// \param msg              - std::string
    ///
    /// \return
    ///        > 0              - actual sended bytes
    ///        <= 0             - failed
    ///
    int send(const std::string& msg);


    ///
    /// \brief receive data from remote host
    ///
    /// \param dat              - data buffer
    /// \param len              - data length
    ///
    /// \return
    ///        > 0              - actual received bytes
    ///        <= 0             - failed
    ///
    int recv(char *dat, int len);
    // int recv(uint8_t *dat, int len);

    ///
    /// \brief receive string from remote host
    ///
    /// \param msg              - std::string
    /// \param maxLen           - maximum string length
    ///
    /// \return
    ///        > 0              - actual received bytes
    ///        <= 0             - failed
    ///
    int recv(std::string& msg, int maxLen = 4096);


    //
    ///
    /// \brief receive data until given data length reached
    ///
    /// \param dat              - data buffer
    /// \param len              - data length
    ///
    /// \return
    ///        > 0              - actual received bytes
    ///        <= 0             - failed
    ///
    int recv_until(uint8_t *dat, int len);


    ////////////////////////////////////////////////////
    /// socket creation & close
    ////////////////////////////////////////////////////

    int create(void);
    int close(void);

    // server functions
    int bind(int port);
    int listen(void);
    int accept(RSocket& s);

    // client functions
    int connect(std::string host, int port);
    int connect(uint32_t addr, int port);

    // get address
    int getMyAddress(RSocketAddress &a);
    int getClientAddress(RSocketAddress &a);
    int getClientAddressArray(std::vector<RSocketAddress> &addr);


    // socket options
    int setNonBlocking(int nb=1);
    int setReuseAddr(uint32_t reuse=1);
    int getRecvBuffSize(void);
    int setRecvBuffSize(int bufsize=4096);

    // status
    int isOpened(void) { return m_sock != -1; }
    bool isSever(void) { return m_server; }


protected:
    int             m_sock;                         ///< socket file descriptor
    int             m_server;                       ///< server or client
    RSocketType     m_socketType;                   ///< socket type
    void*           m_priData;                      ///< socket private data

    int             m_maxConnections;               ///< maximum connections (default: 100)
    int             m_maxHostname;                  ///< maximum length of hostname (default: 1024)

    static int      m_bInitialized;
};


} // end of namespace pi
